<?php

declare(strict_types=1);
/**
 * This file is part of MoChat.
 * @link     https://mo.chat
 * @document https://mochat.wiki
 * @contact  group@mo.chat
 * @license  https://github.com/mochat-cloud/mochat/blob/master/LICENSE
 */
namespace MoChat\App\WorkContact\Logic;

use Hyperf\Di\Annotation\Inject;
use Hyperf\Logger\LoggerFactory;
use Hyperf\Utils\Codec\Json;
use MoChat\App\Corp\Contract\CorpContract;
use MoChat\App\Corp\Utils\WeWorkFactory;
use MoChat\App\WorkContact\Constants\AddWay;
use MoChat\App\WorkContact\Contract\WorkContactContract;
use MoChat\App\WorkContact\Contract\WorkContactEmployeeContract;
use MoChat\App\WorkContact\Event\AddContactEvent;
use MoChat\App\WorkContact\Event\ContactWelcomeEvent;
use MoChat\App\WorkContact\Logic\FieldPivot\UpdatePhoneLogic;
use MoChat\App\WorkContact\Logic\Tag\SyncTagByContactLogic;
use MoChat\App\WorkEmployee\Contract\WorkEmployeeContract;
use MoChat\Plugin\ContactTrack\Constants\EventName;
use MoChat\Plugin\ContactTrack\Contract\ContactTrackContract;
use Psr\EventDispatcher\EventDispatcherInterface;
use Psr\Log\LoggerInterface;

/**
 * 根据员工和客户外部联系人id同步单个客户.
 */
class SyncContactDetailLogic
{
    /**
     * @Inject
     * @var UpdatePhoneLogic
     */
    protected $updatePhoneLogic;

    /**
     * @Inject
     * @var SyncTagByContactLogic
     */
    private $syncTagByContactLogic;

    /**
     * @Inject
     * @var CorpContract
     */
    private $corpService;

    /**
     * @Inject
     * @var WorkEmployeeContract
     */
    private $workEmployeeService;

    /**
     * @Inject
     * @var WorkContactContract
     */
    private $workContactService;

    /**
     * 通讯录 - 客户 - 轨迹互动.
     * @Inject
     * @var ContactTrackContract
     */
    private $contactTrackService;

    /**
     * @Inject
     * @var WorkContactEmployeeContract
     */
    private $workContactEmployeeService;

    /**
     * @var LoggerInterface
     */
    private $logger;

    /**
     * @Inject
     * @var WeWorkFactory
     */
    private $weWorkFactory;

    /**
     * @Inject
     * @var EventDispatcherInterface
     */
    private $eventDispatcher;

    /**
     * @param int|string $corpId 企业授权ID
     * @param int|string $employeeId 跟进员工wxid
     * @param string $wxExternalUserId wx客户id
     * @param array $message 回调事件参数，只有回调调用时才有，手动触发同步时为空
     */
    public function handle($corpId, $employeeId, string $wxExternalUserId, array $message = []): array
    {
        $loggerFactory = make(LoggerFactory::class);
        $this->logger = $loggerFactory->get(__CLASS__);
        $this->logger->debug(sprintf('同步客户详情逻辑开始, 企业ID:%s, 员工ID:%s，客户ID:%s', (string) $corpId, (string) $employeeId, $wxExternalUserId));
        $data = [];

        try {
            // 验证企业是否存在
            if (! ($corp = $this->validateCorp($corpId))) {
                return $data;
            }
            $corpId = (int) $corp['id'];

            // 验证员工是否存在
            if (! ($employee = $this->validateEmployee($corpId, $employeeId))) {
                return $data;
            }
            $employeeId = (int) $employee['id'];

            // 获取微信客户详情
            $wxContactDetail = $this->getWxContactDetail($corpId, $wxExternalUserId);
            if (empty($wxContactDetail)) {
                return $data;
            }

            // 创建或是更新客户
            [$contact, $isNewContact] = $this->createOrUpdateContact($corpId, $employee, $wxContactDetail);
            if (empty($contact)) {
                return $data;
            }

            $contactId = (int) $contact['id'];

            $state = '';
            $welcomeCode = '';
            if (! empty($message)) {
                // 更早的触发发送欢迎语，因为有效期只有20秒
                $state = $message['State'] ?? '';
                $welcomeCode = $message['WelcomeCode'] ?? '';
                $this->triggerContactWelcomeEvent($contact, $employeeId, (string) $state, $isNewContact, $welcomeCode);
            }

            // 创建或更新客户员工关系
            $this->createOrUpdateContactEmployee($corpId, $employee, $contactId, $wxContactDetail);

            // 同步标签
            $this->syncTags($corpId, $employee, $contactId, $wxContactDetail);

            if (! empty($message)) {
                $this->triggerAddContactEvent($contact, $employeeId, (string) $state, $isNewContact, $welcomeCode);
            }
        } catch (\Throwable $e) {
            // 记录错误日志
            $this->logger->error(sprintf('同步或回调客户信息失败，错误原因：%s', $e->getMessage()));
            $this->logger->error($e->getTraceAsString());
        }

        return $data;
    }

    /**
     * 验证企业.
     *
     * @param int|string $corpId 企业id
     *
     * @return array|bool
     */
    private function validateCorp($corpId)
    {
        if (is_string($corpId)) {
            $corp = $this->corpService->getCorpByWxCorpId($corpId, ['id']);
        } else {
            $corp = $this->corpService->getCorpById($corpId, ['id']);
        }

        if (empty($corp)) {
            return false;
        }

        return $corp;
    }

    /**
     * 验证员工.
     *
     * @param int|string $employeeId
     *
     * @return array|bool
     */
    private function validateEmployee(int $corpId, $employeeId)
    {
        if (is_string($employeeId)) {
            $employee = $this->workEmployeeService->getWorkEmployeeByWxUserIdCorpId($employeeId, $corpId, ['id', 'name', 'wx_user_id']);
        } else {
            $employee = $this->workEmployeeService->getWorkEmployeeById($employeeId, ['id', 'name', 'wx_user_id']);
        }

        if (empty($employee)) {
            return false;
        }

        return $employee;
    }

    /**
     * 获取微信客户详情.
     */
    private function getWxContactDetail(int $corpId, string $wxExternalUserId): array
    {
        if (empty($wxExternalUserId)) {
            return [];
        }

        $weWorkContactApp = $this->weWorkFactory->getContactApp($corpId);
        $wxResult = $weWorkContactApp->external_contact->get($wxExternalUserId);

        if ($wxResult['errcode'] !== 0) {
            $this->logger->error(sprintf('获取客户详情出错，企业ID:%s，客户ID:%s，错误信息：%s', (string) $corpId, $wxExternalUserId, $wxResult['errmsg']));
            return [];
        }

        $wxResult['external_contact']['follow_user'] = $wxResult['follow_user'];

        return $wxResult['external_contact'];
    }

    /**
     * 创建或更新客户.
     */
    private function createOrUpdateContact(int $corpId, array $employee, array $wxContact): array
    {
        $isNewContact = 0;
        if (! ($contact = $this->isExistsContact($corpId, $wxContact))) {
            $contactId = $this->createContact($corpId, $employee, $wxContact);
            $isNewContact = 1;
        } else {
            $contactId = $this->updateContact($corpId, $employee, $wxContact, $contact);
        }

        $contact = $this->workContactService->getWorkContactById($contactId);

        return [$contact, $isNewContact];
    }

    private function updateContact(int $corpId, array $employee, array $wxContact, array $contact)
    {
        $updateContractData = [
            'corp_id' => $corpId,
            'wx_external_userid' => $wxContact['external_userid'],
            'name' => $wxContact['name'],
            'avatar' => ! empty($wxContact['avatar']) ? $wxContact['avatar'] : '',
            'type' => $wxContact['type'],
            'gender' => $wxContact['gender'],
            'unionid' => isset($wxContact['unionid']) ? $wxContact['unionid'] : '',
            'position' => isset($wxContact['position']) ? $wxContact['position'] : '',
            'corp_name' => isset($wxContact['corp_name']) ? $wxContact['corp_name'] : '',
            'corp_full_name' => isset($wxContact['corp_full_name']) ? $wxContact['corp_full_name'] : '',
            'external_profile' => isset($wxContact['external_profile']) ? json_encode($wxContact['external_profile']) : json_encode([]),
            'business_no' => isset($wxContact['business_no']) ? $wxContact['business_no'] : '',
            'updated_at' => date('Y-m-d H:i:s'),
            'deleted_at' => null,
        ];

        $this->workContactService->updateWorkContactById((int) $contact['id'], $updateContractData, true);
        return (int) $contact['id'];
    }

    /**
     * 判读是否存在该客户.
     *
     * @return array
     */
    private function isExistsContact(int $corpId, array $wxContactDetail)
    {
        // 查询当前公司是否存在此客户
        $contact = $this->workContactService->getWorkContactByCorpIdWxExternalUserId($corpId, $wxContactDetail['external_userid'], ['id', 'deleted_at'], true);

        if (empty($contact)) {
            return [];
        }

        return $contact;
    }

    /**
     * 创建客户.
     *
     * @return int
     */
    private function createContact(int $corpId, array $employee, array $wxContact)
    {
        $createContractData = [
            'corp_id' => $corpId,
            'wx_external_userid' => $wxContact['external_userid'],
            'name' => $wxContact['name'],
            'avatar' => ! empty($wxContact['avatar']) ? $wxContact['avatar'] : '',
            'type' => $wxContact['type'],
            'gender' => $wxContact['gender'],
            'unionid' => isset($wxContact['unionid']) ? $wxContact['unionid'] : '',
            'position' => isset($wxContact['position']) ? $wxContact['position'] : '',
            'corp_name' => isset($wxContact['corp_name']) ? $wxContact['corp_name'] : '',
            'corp_full_name' => isset($wxContact['corp_full_name']) ? $wxContact['corp_full_name'] : '',
            'external_profile' => isset($wxContact['external_profile']) ? json_encode($wxContact['external_profile']) : json_encode([]),
            'business_no' => isset($wxContact['business_no']) ? $wxContact['business_no'] : '',
            'created_at' => date('Y-m-d H:i:s'),
        ];

        return $this->workContactService->createWorkContact($createContractData);
    }

    /**
     * 创建或更新客户员工关联表.
     */
    private function createOrUpdateContactEmployee(int $corpId, array $employee, int $contactId, array $wxContact)
    {
        $contactEmployee = $this->isExistsContactEmployee((int) $employee['id'], $contactId);
        if (empty($contactEmployee)) {
            $this->createContactEmployee($corpId, $employee, $contactId, $wxContact);
        } else {
            $this->updateContactEmployee($corpId, $employee, $contactId, $wxContact, $contactEmployee);
        }

        if (empty($contactEmployee) || ! empty($contactEmployee['deletedAt'])) {
            $followEmployee = $this->getCurrentFollowEmployee($employee['wxUserId'], $wxContact['follow_user']);
            $addWay = isset($followEmployee['add_way']) ? $followEmployee['add_way'] : 0;
            $this->createContactTrack($corpId, $contactId, (int) $employee['id'], $employee['name'], $addWay);
        }
    }

    /**
     * 判断是否存在客户员工关系.
     *
     * @return array
     */
    private function isExistsContactEmployee(int $employeeId, int $contactId)
    {
        // 查询当前用户与客户是否存在关联信息
        $workContactEmployee = $this->workContactEmployeeService->findWorkContactEmployeeByOtherIds($employeeId, $contactId, ['id', 'deleted_at'], true);

        if (empty($workContactEmployee)) {
            return [];
        }

        return $workContactEmployee;
    }

    /**
     * 创建跟进员工中间表信息.
     *
     * @param int $employeeId
     * @param array $followEmployee
     */
    private function createContactEmployee(int $corpId, array $employee, int $contactId, array $wxContact)
    {
        $followEmployee = [];
        if (! empty($wxContact['follow_user'])) {
            $followEmployee = $this->getCurrentFollowEmployee($employee['wxUserId'], $wxContact['follow_user']);
        }

        // 组织客户与企业用户关联表信息
        $createContractEmployeeData = [
            'employee_id' => (int) $employee['id'],
            'contact_id' => $contactId,
            'remark' => $followEmployee['remark'] ?? '',
            'description' => $followEmployee['description'] ?? '',
            'remark_corp_name' => $followEmployee['remark_corp_name'] ?? '',
            'remark_mobiles' => isset($followEmployee['remark_mobiles']) ? Json::encode($followEmployee['remark_mobiles']) : Json::encode([]),
            'add_way' => $followEmployee['add_way'] ?? 0,
            'oper_userid' => $followEmployee['oper_userid'] ?? '',
            'state' => $followEmployee['state'] ?? '',
            'corp_id' => $corpId,
            'status' => 1, // 正常
            'create_time' => isset($followEmployee['createtime']) ? date('Y-m-d H:i:s', $followEmployee['createtime']) : '',
            'created_at' => date('Y-m-d H:i:s'),
        ];
        $this->workContactEmployeeService->createWorkContactEmployee($createContractEmployeeData);
        // 更新客户画像-手机号
        $phoneData = ['contactId' => $contactId, 'employeeId' => (int) $employee['id'], 'remarkMobiles' => $followEmployee['remark_mobiles']];
        $this->updatePhoneLogic->handle($phoneData);
    }

    /**
     * 创建跟进员工中间表信息.
     *
     * @return array
     */
    private function updateContactEmployee(int $corpId, array $employee, int $contactId, array $wxContact, array $contactEmployee)
    {
        try {
            $followEmployee = [];
            if (! empty($wxContact['follow_user'])) {
                $followEmployee = $this->getCurrentFollowEmployee($employee['wxUserId'], $wxContact['follow_user']);
            }
            // 组织客户与企业用户关联表信息
            $addWay = isset($followEmployee['add_way']) ? $followEmployee['add_way'] : 0;
            $updateContractEmployeeData = [
                'employee_id' => (int) $employee['id'],
                'contact_id' => $contactId,
                'remark' => $followEmployee['remark'] ?? '',
                'description' => $followEmployee['description'] ?? '',
                'remark_corp_name' => $followEmployee['remark_corp_name'] ?? '',
                'remark_mobiles' => isset($followEmployee['remark_mobiles']) ? Json::encode($followEmployee['remark_mobiles']) : Json::encode([]),
                'add_way' => $addWay,
                'oper_userid' => $followEmployee['oper_userid'] ?? '',
                'state' => $followEmployee['state'] ?? '',
                'corp_id' => $corpId,
                'status' => 1, // 正常
                'create_time' => isset($followEmployee['createtime']) ? date('Y-m-d H:i:s', $followEmployee['createtime']) : '',
                'updated_at' => date('Y-m-d H:i:s'),
                'deleted_at' => null,
            ];
            $this->workContactEmployeeService->updateWorkContactEmployeeById((int) $contactEmployee['id'], $updateContractEmployeeData, true);
            // 更新客户画像-手机号
            $phoneData = ['contactId' => $contactId, 'employeeId' => (int) $employee['id'], 'remarkMobiles' => $followEmployee['remark_mobiles']];
            $this->updatePhoneLogic->handle($phoneData);
        } catch (\Throwable $e) {
            $this->logger->error(sprintf('更新客户详情出错，企业ID:%s，客户ID:%s，错误信息：%s', (string) $corpId, (string) $contactId, $e->getMessage()));
            return [];
        }
    }

    /**
     * 获取当前跟进员工.
     *
     * @return array|mixed
     */
    private function getCurrentFollowEmployee(string $wxUserId, array $followUser)
    {
        foreach ($followUser as $item) {
            if ($wxUserId === $item['userid']) {
                return $item;
            }
        }

        return [];
    }

    /**
     * 创建客户轨迹.
     */
    private function createContactTrack(int $corpId, int $contactId, int $employeeId, string $employeeName, int $addWay)
    {
        $jsonContact = [
            'title' => EventName::CREATE,
            'content' => sprintf('客户通过%s添加企业成员【%s】', AddWay::getMessage($addWay), $employeeName),
        ];
        // 创建客户事件
        $contactEmployeeTrackData = [
            'employee_id' => $employeeId,
            'contact_id' => $contactId,
            'event' => \MoChat\Plugin\ContactTrack\Constants\Event::CREATE,
            'content' => Json::encode($jsonContact),
            'corp_id' => $corpId,
            'created_at' => date('Y-m-d H:i:s'),
            'updated_at' => date('Y-m-d H:i:s'),
        ];
        // 记录日志
        $this->contactTrackService->createContactTrack($contactEmployeeTrackData);
    }

    /**
     * 同步标签.
     */
    private function syncTags(int $corpId, array $employee, int $contactId, array $wxContact)
    {
        if (empty($wxContact['follow_user'])) {
            return;
        }

        $followEmployee = $this->getCurrentFollowEmployee($employee['wxUserId'], $wxContact['follow_user']);
        if (isset($followEmployee['tags']) && ! empty($followEmployee['tags'])) {
            $this->syncTagByContactLogic->handle($corpId, $contactId, (int) $employee['id'], $followEmployee['tags']);
        }
    }

    /**
     * 触发发送欢迎语事件.
     */
    private function triggerContactWelcomeEvent(
        array $contact,
        int $employeeId,
        string $state,
        int $isNewContact,
        string $welcomeCode
    ) {
        if (empty($contact)) {
            return;
        }

        $contact['employeeId'] = $employeeId;
        $contact['state'] = $state;
        $contact['isNew'] = $isNewContact;
        $contact['welcomeCode'] = $welcomeCode;

        go(function () use ($contact) {
            $this->eventDispatcher->dispatch(new ContactWelcomeEvent($contact));
        });
    }

    /**
     * 触发添加客户事件.
     */
    private function triggerAddContactEvent(array $contact, int $employeeId, string $state, int $isNewContact, string $welcomeCode)
    {
        if (empty($contact)) {
            return;
        }

        $contact['employeeId'] = $employeeId;
        $contact['state'] = $state;
        $contact['isNew'] = $isNewContact;
        $contact['welcomeCode'] = $welcomeCode;

        go(function () use ($contact) {
            $this->eventDispatcher->dispatch(new AddContactEvent($contact));
        });
    }
}
